package com.hrpc.config;

import com.hrpc.IdGenerate;
import com.hrpc.ProtocalConfig;
import com.hrpc.compress.Compressor;
import com.hrpc.compress.CompressorFactory;
import com.hrpc.discovery.RegisterConfig;
import com.hrpc.loadbalancer.LoadBalancer;
import com.hrpc.serialize.Serialize;
import com.hrpc.serialize.SerializeFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

public class XmlReslover {
    /**
     * 从配置文件读取配置信息
     * @param configuration
     */
    public void loadFromXml(Configuration configuration) {
        //1.创建一个document
        try {
            DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
            factory.setValidating(false);
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            DocumentBuilder builder=factory.newDocumentBuilder();
            InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("hrpc.xml");
            Document document = builder.parse(inputStream);
            //2.获取一个xpath解析器
            XPathFactory xPathFactory=XPathFactory.newInstance();
            XPath xPath = xPathFactory.newXPath();
            //3.解析所有的标签
            configuration.setPort(resolverPort(document,xPath));
            configuration.setAppName(resolverAppName(document,xPath));
            configuration.setId_generate(resolverIdGenerate(document,xPath));
            configuration.setRegisterConfig(resolverRegisterConfig(document,xPath));
            /**
             * 需要独立的处理
             */
            configuration.setSerializeType(resolverSerializeType(document,xPath));
            configuration.setCompressType(resolverCompressType(document,xPath));
            configuration.setProtocalConfig(resolverProtocalConfig(document,xPath));
            configuration.setLoad_balancer(resolverLoadBalancer(document,xPath));
            configuration.setProtocalConfig(new ProtocalConfig(configuration.getSerializeType()));
            //配置新的压缩方式和序列化方式，并将其纳入工厂中
            ObjectWrapper<Compressor> compressorObjectWrapper = resolverCompressor(document, xPath);
            CompressorFactory.addCompressor(compressorObjectWrapper);
            //处理配置的序列化方式
            ObjectWrapper<Serialize> serializeObjectWrapper = resolverSerializer(document, xPath);
            SerializeFactory.addSerializer(serializeObjectWrapper);
        } catch (SAXException | IOException | ParserConfigurationException e) {
            throw new RuntimeException(e);
        }


    }

    private ObjectWrapper<Serialize> resolverSerializer(Document document, XPath xPath) {
        String expression="/configuration/serializer";
        Serialize serialize = parseObject(document, xPath, expression, null);
        Byte code =  Byte.valueOf(Objects.requireNonNull(parseString(document, xPath, expression, "code")));
        String name = parseString(document, xPath, expression, "name");
        return new ObjectWrapper<>(code,name,serialize);
    }

    private ProtocalConfig resolverProtocalConfig(Document document, XPath xPath) {

        return null;
    }

    /**
     * 解析压缩的具体实现
     * @param document
     * @param xPath
     * @return
     */
    private ObjectWrapper<Compressor> resolverCompressor(Document document, XPath xPath) {
        String expression="/configuration/compressor";
        Compressor compressor = parseObject(document, xPath, expression, null);
        Byte code =  Byte.valueOf(Objects.requireNonNull(parseString(document, xPath, expression, "code")));
        String name = parseString(document, xPath, expression, "name");
        ObjectWrapper<Compressor> compressorObjectWrapper=new ObjectWrapper<>(code,name,compressor);
        return compressorObjectWrapper;
    }

    private String resolverCompressType(Document document, XPath xPath) {
        String expression="/configuration/compressType";
        return parseString(document,xPath,expression,"type");
    }

    /**
     * 解析序列化的方式
     * @param document
     * @param xPath
     * @return
     */
    private String resolverSerializeType(Document document, XPath xPath) {
        String expression="/configuration/serializeType";
        return parseString(document,xPath,expression,"type");
    }

    /**
     * 解析注册中心
     * @param document
     * @param xPath
     * @return
     */
    private RegisterConfig resolverRegisterConfig(Document document, XPath xPath) {
        String expression="/configuration/register";
        String url = parseString(document, xPath, expression,"url");
        return new RegisterConfig(url);
    }

    private IdGenerate resolverIdGenerate(Document document, XPath xPath) {
        String expression="/configuration/idGenerator";
        String aClass = parseString(document, xPath, expression, "class");
        String dataCenterId = parseString(document, xPath, expression, "dataCenterId");
        String machineId = parseString(document, xPath, expression, "MachineId");
        Object instance = null;
        try {
            Class<?> clazz = Class.forName(aClass);
            instance = clazz.getConstructor(new Class[]{long.class, long.class})
                    .newInstance(Long.parseLong(dataCenterId), Long.parseLong(machineId));
            return (IdGenerate) instance;
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private String resolverAppName(Document document, XPath xPath) {
        String expression="/configuration/appName";
        String appName=parseString(document,xPath,expression);
        return appName;
    }

    private LoadBalancer resolverLoadBalancer(Document document, XPath xPath) {
        String expression="/configuration/loadBalancer";
        return parseObject(document,xPath,expression,null);
    }

    /**
     * 解析端口号
     * @param document
     * @param xPath
     * @return
     */
    private int resolverPort(Document document, XPath xPath) {
        String expression="/configuration/port";
        String portString=parseString(document,xPath,expression);
        return Integer.parseInt(portString);
    }

    /**
     * 获得一个节点属性的值
     * @param document
     * @param xPath
     * @param expre
     * @return
     */
    private String parseString(Document document, XPath xPath,String expre) {
        try {
            XPathExpression expression = xPath.compile(expre);
            Node targetNode = (Node) expression.evaluate(document, XPathConstants.NODE);
            return targetNode.getTextContent();
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 获得一个节点属性的值
     * @param document
     * @param xPath
     * @param expre
     * @param AttributeName
     * @return
     */
    private String parseString(Document document, XPath xPath,String expre,String AttributeName) {
        try {
            XPathExpression expression = xPath.compile(expre);
            Node targetNode = (Node) expression.evaluate(document, XPathConstants.NODE);
            return targetNode.getAttributes().getNamedItem(AttributeName).getNodeValue();
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        }
    }



    /**
     * 解析一个节点，返回一个实例
     * @param document 文档对象
     * @param xPath XPath解析器
     * @param expre XPath表达式
     * @param paramType 参数类型
     * @param params 参数
     * @return 配置的实例
     * @param <T>
     * @throws XPathExpressionException
     */
    private <T> T parseObject(Document document, XPath xPath,String expre,Class<?>[] paramType,Object... params) {
        try {
            XPathExpression expression = xPath.compile(expre);
            Node targetName = (Node) expression.evaluate(document, XPathConstants.NODE);
            String className = targetName.getAttributes().getNamedItem("class").getNodeValue();
            Class<?> aClass = Class.forName(className);
            Object instance=null;
            if (paramType == null){
                instance = aClass.getConstructor().newInstance();
            }else {
                instance = aClass.getConstructor(paramType).newInstance(params);
            }
            return (T)instance;
        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
                 InvocationTargetException | XPathExpressionException e) {
            throw new RuntimeException(e);
        }

    }
}
